package cn.iocoder.yudao.module.system.service.examcontrolline;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.examcontrolline.vo.*;
import cn.iocoder.yudao.module.system.convert.examcontrolline.ExamControlLineConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.examcontrolline.ExamControlLineDO;
import cn.iocoder.yudao.module.system.dal.dataobject.examscoreconversion.ExamScoreConversionDO;
import cn.iocoder.yudao.module.system.dal.dataobject.examscore.ExamScoreDO;
import cn.iocoder.yudao.module.system.dal.mysql.examcontrolline.ExamControlLineMapper;
import cn.iocoder.yudao.module.system.dal.mysql.examscoreconversion.ExamScoreConversionMapper;
import cn.iocoder.yudao.module.system.dal.mysql.examscore.ExamScoreMapper;
import cn.iocoder.yudao.module.system.service.equivalentscorerank.EquivalentScoreRankService;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 特控线配置 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class ExamControlLineServiceImpl implements ExamControlLineService {

    @Resource
    private ExamControlLineMapper examControlLineMapper;
    
    @Resource
    private ExamScoreConversionMapper examScoreConversionMapper;
    
    @Resource
    private ExamScoreMapper examScoreMapper;
    
    @Resource
    @Lazy
    private EquivalentScoreRankService equivalentScoreRankService;

    @Override
    public Long createExamControlLine(@Valid ExamControlLineCreateReqVO createReqVO) {
        // 校验考试和线类型组合是否已存在
        validateExamControlLineExists(createReqVO.getExamId(), createReqVO.getLineType(), null);
        
        // 转换为DO对象
        ExamControlLineDO examControlLine = ExamControlLineConvert.INSTANCE.convert(createReqVO);
        
        // 设置当前登录用户的部门ID（用于数据权限控制）
        Long currentUserDeptId = SecurityFrameworkUtils.getLoginUserDeptId();
        if (currentUserDeptId != null) {
            examControlLine.setDeptId(currentUserDeptId);
            log.debug("设置特控线部门ID: {}, 考试ID: {}, 线类型: {}", currentUserDeptId, createReqVO.getExamId(), createReqVO.getLineType());
        } else {
            log.warn("当前登录用户未设置部门ID，特控线将不设置部门信息: 考试ID={}, 线类型={}", createReqVO.getExamId(), createReqVO.getLineType());
        }
        
        // 自动计算省控线
        calculateAndSetProvinceControlLine(examControlLine);
        
        // 插入
        examControlLineMapper.insert(examControlLine);
        
        // 返回
        return examControlLine.getId();
    }

    @Override
    public void updateExamControlLine(@Valid ExamControlLineUpdateReqVO updateReqVO) {
        // 校验存在
        validateExamControlLineExists(updateReqVO.getId());
        
        // 转换为DO对象
        ExamControlLineDO updateObj = ExamControlLineConvert.INSTANCE.convert(updateReqVO);
        
        // 设置当前登录用户的部门ID（用于数据权限控制）
        Long currentUserDeptId = SecurityFrameworkUtils.getLoginUserDeptId();
        if (currentUserDeptId != null) {
            updateObj.setDeptId(currentUserDeptId);
            log.debug("更新特控线部门ID: {}, 特控线ID: {}", currentUserDeptId, updateReqVO.getId());
        } else {
            log.warn("当前登录用户未设置部门ID，特控线将不设置部门信息: 特控线ID={}", updateReqVO.getId());
        }
        
        // 自动计算省控线
        calculateAndSetProvinceControlLine(updateObj);
        
        // 更新
        examControlLineMapper.updateById(updateObj);
    }

    @Override
    public void deleteExamControlLine(Long id) {
        // 校验存在
        validateExamControlLineExists(id);
        
        // 删除
        examControlLineMapper.deleteById(id);
    }

    private ExamControlLineDO validateExamControlLineExists(Long id) {
        ExamControlLineDO examControlLine = examControlLineMapper.selectById(id);
        if (examControlLine == null) {
            throw exception(EXAM_CONTROL_LINE_NOT_EXISTS);
        }
        return examControlLine;
    }

    private void validateExamControlLineExists(Long examId, String lineType, Long excludeId) {
        ExamControlLineDO examControlLine = examControlLineMapper.selectByExamIdAndLineType(examId, lineType);
        if (examControlLine != null && !Objects.equals(examControlLine.getId(), excludeId)) {
            throw exception(EXAM_CONTROL_LINE_EXISTS);
        }
    }

    @Override
    public ExamControlLineDO getExamControlLine(Long id) {
        return examControlLineMapper.selectById(id);
    }

    @Override
    public List<ExamControlLineDO> getExamControlLineList(Collection<Long> ids) {
        return examControlLineMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<ExamControlLineDO> getExamControlLinePage(ExamControlLinePageReqVO pageReqVO) {
        return examControlLineMapper.selectPage(pageReqVO);
    }

    @Override
    public List<ExamControlLineDO> getExamControlLineList(ExamControlLinePageReqVO exportReqVO) {
        return examControlLineMapper.selectList(exportReqVO);
    }

    @Override
    public List<ExamControlLineDO> getExamControlLineListByExamId(Long examId) {
        return examControlLineMapper.selectListByExamId(examId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ExamScoreConversionRespVO convertScore(Long examId, Long studentId, BigDecimal originalScore, Long controlLineId) {
        // 获取特控线信息
        ExamControlLineDO controlLine = validateExamControlLineExists(controlLineId);
        
        // 计算换算后成绩
        BigDecimal convertedScore = originalScore.multiply(controlLine.getConversionRatio())
                .setScale(2, RoundingMode.HALF_UP);
        
        // 计算预估位次（这里是简化计算，实际应该根据历史数据）
        Integer estimatedRank = calculateEstimatedRank(convertedScore, controlLine.getProvince());
        
        // 保存换算记录
        ExamScoreConversionDO conversionRecord = ExamScoreConversionDO.builder()
                .examId(examId)
                .studentId(studentId)
                .originalScore(originalScore)
                .convertedScore(convertedScore)
                .estimatedRank(estimatedRank)
                .controlLineId(controlLineId)
                .conversionDate(LocalDateTime.now())
                .build();
        
        // 删除已存在的记录
        ExamScoreConversionDO existingRecord = examScoreConversionMapper.selectByExamIdAndStudentId(examId, studentId);
        if (existingRecord != null) {
            examScoreConversionMapper.deleteById(existingRecord.getId());
        }
        
        examScoreConversionMapper.insert(conversionRecord);
        
        // 构建返回结果
        ExamScoreConversionRespVO result = ExamControlLineConvert.INSTANCE.convert(conversionRecord);
        result.setControlLineName(controlLine.getLineName());
        result.setOverControlLineScore(convertedScore.subtract(controlLine.getScoreLine()));
        result.setAdmissionProbabilityLevel(calculateAdmissionProbabilityLevel(result.getOverControlLineScore()));
        
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<ExamScoreConversionRespVO> batchConvertScore(Long examId, Long controlLineId) {
        // 获取考试的所有成绩
        List<ExamScoreDO> examScores = examScoreMapper.selectListByExamId(examId);
        
        List<ExamScoreConversionRespVO> results = new ArrayList<>();
        for (ExamScoreDO examScore : examScores) {
            try {
                ExamScoreConversionRespVO result = convertScore(examId, examScore.getStudentId(), 
                        examScore.getTotalScore(), controlLineId);
                result.setStudentName(examScore.getStudentName());
                result.setStudentNo(examScore.getStudentNo());
                results.add(result);
            } catch (Exception e) {
                log.error("批量换算成绩失败，学生ID：{}，错误：{}", examScore.getStudentId(), e.getMessage());
            }
        }
        
        return results;
    }

    @Override
    public List<ExamScoreConversionDO> getConversionRecordsByStudentId(Long studentId) {
        return examScoreConversionMapper.selectListByStudentId(studentId);
    }

    @Override
    public List<CollegeRecommendationRespVO> getCollegeRecommendations(BigDecimal convertedScore, Integer estimatedRank, String province) {
        // 这里应该调用院校推荐服务，暂时返回空列表
        // TODO: 实现院校推荐逻辑
        return new ArrayList<>();
    }

    @Override
    public List<MajorRecommendationRespVO> getMajorRecommendations(BigDecimal convertedScore, Integer estimatedRank, String province, String subjectCombination) {
        // 这里应该调用专业推荐服务，暂时返回空列表
        // TODO: 实现专业推荐逻辑
        return new ArrayList<>();
    }

    @Override
    public StudentAdmissionAdviceRespVO generateAdmissionAdvice(Long studentId, Long examId) {
        // 这里应该生成学生报考建议，暂时返回空对象
        // TODO: 实现报考建议生成逻辑
        return new StudentAdmissionAdviceRespVO();
    }

    /**
     * 计算预估位次
     */
    private Integer calculateEstimatedRank(BigDecimal convertedScore, String province) {
        // 简化计算，实际应该根据历史数据和统计模型
        // 这里假设每10分对应1000个位次
        return Math.max(1, (int) ((750 - convertedScore.doubleValue()) * 100));
    }

    /**
     * 计算录取概率等级
     */
    private String calculateAdmissionProbabilityLevel(BigDecimal overControlLineScore) {
        if (overControlLineScore.compareTo(BigDecimal.valueOf(50)) >= 0) {
            return "冲刺";
        } else if (overControlLineScore.compareTo(BigDecimal.valueOf(20)) >= 0) {
            return "稳妥";
        } else if (overControlLineScore.compareTo(BigDecimal.valueOf(-10)) >= 0) {
            return "保底";
        } else {
            return "风险";
        }
    }

    /**
     * 计算并设置省控线
     * 根据特控线的省份、年份同时计算物理类和历史类的省控线
     */
    private void calculateAndSetProvinceControlLine(ExamControlLineDO examControlLine) {
        try {
            // 检查必要字段是否存在
            if (examControlLine.getProvince() == null || examControlLine.getYear() == null) {
                log.warn("省控线计算失败：缺少必要参数 - province={}, year={}", 
                        examControlLine.getProvince(), examControlLine.getYear());
                return;
            }
            
            String province = examControlLine.getProvince();
            Integer year = examControlLine.getYear();
            
            // 计算物理类省控线
            if (examControlLine.getPhysicsScoreLine() != null) {
                Map<Integer, BigDecimal> physicsControlLines = equivalentScoreRankService.getProvinceControlLines(
                        province, "物理类", Arrays.asList(year));
                
                BigDecimal physicsProvinceControlLine = physicsControlLines.get(year);
                if (physicsProvinceControlLine != null) {
                    examControlLine.setPhysicsProvinceControlLine(physicsProvinceControlLine);
                    log.info("成功计算物理类省控线：province={}, year={}, provinceControlLine={}", 
                            province, year, physicsProvinceControlLine);
                } else {
                    log.warn("未找到物理类省控线数据：province={}, year={}", province, year);
                }
            }
            
            // 计算历史类省控线
            if (examControlLine.getHistoryScoreLine() != null) {
                Map<Integer, BigDecimal> historyControlLines = equivalentScoreRankService.getProvinceControlLines(
                        province, "历史类", Arrays.asList(year));
                
                BigDecimal historyProvinceControlLine = historyControlLines.get(year);
                if (historyProvinceControlLine != null) {
                    examControlLine.setHistoryProvinceControlLine(historyProvinceControlLine);
                    log.info("成功计算历史类省控线：province={}, year={}, provinceControlLine={}", 
                            province, year, historyProvinceControlLine);
                } else {
                    log.warn("未找到历史类省控线数据：province={}, year={}", province, year);
                }
            }
        } catch (Exception e) {
            log.error("计算省控线时发生异常：province={}, year={}", 
                    examControlLine.getProvince(), examControlLine.getYear(), e);
        }
    }

}
